/****************************************************************************
   Copyright 2013 Stacy Doss

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
****************************************************************************/

%{

#include <stdarg.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>

#include "Spef.h"
#include "SpefPort.h"
#include "SpefParValue.h"

#include "SpefParserBase.h"

#define ____YYSTYPE std::string


//#define YYDEBUG 1

// stuff from flex that bison needs to know about:
//extern "C" int yylex();
//extern "C" int yyparse();

extern "C" FILE *yyin;
 

int yydebug = 1;
int indent  = 0;
char *iden_dum;

int yylex (void);
static void yyerror (const char *msg);

spef::Spef* root_spef = new spef::Spef();
spef::SpefPort* port_entry;
ParValue*       port_cap;
ParValueVector* port_slews;

%}

%union {
    int                   i;
    float                 f;
    char*                 s;

    struct ParValue       *par;
    struct ParValueVector *vpar;
}

%token <s> COLON
%token <s> BACKSLASH
%token <s> PERIOD
%token <s> PIPE
%token <s> RIGHTBRACKET
%token <s> RIGHTBRACE
%token <s> RIGHTPAREN
%token <s> RIGHTANGLE
%token <s> LEFTBRACKET
%token <s> LEFTBRACE
%token <s> LEFTPAREN
%token <s> LEFTANGLE
%token <s> NAME
%token <i> INT
%token <f> FLOAT
%token <s> QSTRING
%token SPEF
%token DESIGN
%token DATE
%token VENDOR
%token PROGRAM
%token VERSION
%token DESIGN_FLOW
%token <s> DIVIDER
%token <s> DELIMITER
%token <s> BUS_DELIMITER
%token <s> T_UNIT
%token <s> C_UNIT
%token <s> R_UNIT
%token <s> L_UNIT
%token <s> NS
%token <s> PS
%token <s> PF
%token <s> FF
%token <s> OHM
%token <s> KOHM
%token <s> HENRY
%token <s> MH
%token <s> UH
%token NAMEMAP
%token PORTS
%token INPUT
%token OUTPUT
%token BIDI
%token LOAD
%token SLEW
%token COORD
%token DRIVER
%token DNET
%token CONN
%token CAP
%token RES
%token VCONF
%token PORT
%token INTERNAL
%token END
%token INDUCT
%token <s> INDEX


%type <s>   hier_delim
%type <s>   pin_delim
%type <s>   prefix_bus_delim
%type <s>   suffix_bus_delim
%type <s>   hchar1
%type <s>   hchar2

//%type <s>   index
//%type <s>   name
//%type <s>   direction
%type <s>   time_unit
%type <s>   cap_unit
%type <s>   res_unit
%type <s>   induct_unit
%type <s>   mapped_item
%type <s>   name
%type <s>   path
%type <s>   index
%type <s>   pathpart
%type <s>   identifier
%type <s>   port_name
%type <s>   direction
%type <par>   cap_load
%type <vpar>  slews
%type <par>  par_value

%% /* Grammar rules and actions follow */
spef:
				header_items statements;

hchar1:
				COLON;

hchar2:
				BACKSLASH
				| PERIOD
				| PIPE;

identifier:
				NAME;
				
name:
				QSTRING 
				| identifier;

statements:		
				| statements statement ;
				
header_items:
				| header_items header_item;
	
header_item:
				spef_version
				| design_name 
				| date vendor
				| program
				| version
				| design_flow
				| hierarchy_div_def
				| pin_delim_def
				| bus_delim_def
				| unit_def;
	
spef_version: 
				SPEF QSTRING {
					root_spef->set_header((char *)"spef", $2);
				};
				
design_name:
         		DESIGN QSTRING {
					root_spef->set_header((char *)"design", $2);
				}

date:
         		DATE QSTRING {
					root_spef->set_header((char *)"date", $2);
				}

vendor:
         		VENDOR QSTRING {
					root_spef->set_header((char *)"vendor", $2);
				} 

program:
         		PROGRAM QSTRING {
					root_spef->set_header((char *)"program", $2);
				} 

version:
         		VERSION QSTRING {
					root_spef->set_header((char *)"version", $2);
				} 

design_flow:
         		DESIGN_FLOW QSTRING {
					root_spef->set_header((char *)"flow", $2);
				};

hierarchy_div_def:
			 	DIVIDER hier_delim {
					root_spef->set_header((char *)"hierarchy", $2);
				};

pin_delim_def:
				DELIMITER pin_delim {
					root_spef->set_header((char *)"pin", $2);
				};

bus_delim_def: 
				BUS_DELIMITER prefix_bus_delim suffix_bus_delim {
					root_spef->set_header((char *)"bus_prefix", $2);
					root_spef->set_header((char *)"bus_suffix", $3);
				};
			
hier_delim: 
				hchar2 {$$ = $1};
//				hchar {$$ = $1};
		
pin_delim:
				hchar1 {$$ = $1};
//				hchar {$$ = $1};

//bus_delim:
//				prefix_bus_delim | suffix_bus_delim;

prefix_bus_delim:
				LEFTBRACE | LEFTBRACKET | LEFTPAREN | LEFTANGLE ;
//				LEFTBRACE | LEFTBRACKET | LEFTPAREN | LEFTANGLE | COLON | PERIOD;

suffix_bus_delim:
				RIGHTBRACE | RIGHTBRACKET | RIGHTPAREN | RIGHTANGLE;

unit_def:
				time_scale cap_scale res_scale induc_scale;

time_scale:
				T_UNIT INT time_unit {
					root_spef->set_scale((char *)"time", $2, $3);
				};

cap_scale:
				C_UNIT INT cap_unit {
					root_spef->set_scale((char *)"cap", $2, $3);
				};

res_scale:
				R_UNIT INT res_unit {
					root_spef->set_scale((char *)"res", $2, $3);
				};

induc_scale:
				L_UNIT INT induct_unit {
					root_spef->set_scale((char *)"induct", $2, $3);
				};

time_unit:
				NS     {$$ = (char *)"NS"} 
				| PS   {$$ = (char *)"PS"};

cap_unit:
				PF     {$$ = (char *)"PF"}
				| FF   {$$ = (char *)"FF"};

res_unit:
				OHM    {$$ = (char *)"OHM"} 
				| KOHM {$$ = (char *)"KOHM"};

induct_unit:
				HENRY  {$$ = (char *)"HENRY"} 
				| MH   {$$ = (char *)"MH"} 
				| UH   {$$ = (char *)"UH"};

statement:
				name_map
				| port_def
				| d_net;

name_map:
				NAMEMAP name_map_entries;
				
name_map_entries:
				name_map_entry
				| name_map_entry name_map_entries ;

name_map_entry:
				index mapped_item {
					root_spef->add_name_map($1, $2);
				};

index:
				INDEX;
				
mapped_item:
				path;

pathpart:
				name hier_delim;

path:			name
				| pathpart name;

port_def:
				PORTS port_entries;
				
port_entries:
				port_entry {printf("d %s\n", port_entry->get_direction().c_str())}
				| port_entries port_entry {printf("d %s\n", port_entry->get_direction().c_str())};

port_entry:
				port_name {
					port_entry = new spef::SpefPort();
					port_entry->set_id($1);
				} 
				direction port_attributes	{
					port_entry->set_direction($3);
				};
				
port_name:
				index
				| path ;
				
direction:
				INPUT    {$$ = (char*)"input"}
				| OUTPUT {$$ = (char*)"output"}
				| BIDI   {$$ = (char*)"inout"};

port_attributes:
				| port_attributes port_attribute;
				
port_attribute:
				cap_load {
							spef::SpefParValue*  par  = new spef::SpefParValue();
							for(std::vector<double>::iterator it = $1->values->begin(); it != $1->values->end(); ++it) {
							    par->add_value(*it);
							}
							port_entry->set_cap(par);
						 } 
				| slews  {
							std::vector<spef::SpefParValue*>* pars = new std::vector<spef::SpefParValue*>();
							for(std::vector<ParValue*>::iterator oit = $1->values->begin(); oit != $1->values->end(); ++oit) {
								spef::SpefParValue*  par  = new spef::SpefParValue();

								for(std::vector<double>::iterator it = (*oit)->values->begin(); it != (*oit)->values->end(); ++it) {
								    par->add_value(*it);
							    }

							    pars->push_back(par);
							}

							port_entry->set_slews(pars);
						 };

cap_load:
				LOAD par_value {$$ = $2};

slews:
				SLEW par_value par_value {
					ParValueVector* pars = new ParValueVector();
					pars->values->push_back($2);
					pars->values->push_back($3);
					$$ = pars;
				};

par_value:		FLOAT {
						ParValue* val = new ParValue();
						val->values->push_back($1);
						$$ = val;
					  }
				| FLOAT COLON FLOAT COLON FLOAT {
						ParValue* val = new ParValue();
						val->values->push_back($1);
						val->values->push_back($3);
						val->values->push_back($5);
						$$ = val;
					  };

d_net:
				DNET net_ref total_cap routing_conf conn_sec cap_sec res_sec END;

net_ref:
				index
				| path;

total_cap:
				par_value;
				
routing_conf:
				|
				VCONF conf;
				
conf:           INT;

conn_sec:
				|
				CONN conn_defs;

conn_defs:
				conn_def
				| conn_defs conn_def;

conn_def:
				conn_type connection direction conn_attrs;

conn_type:
				PORT
				| INTERNAL ;

connection:
				index pin_delim index
				| index pin_delim name
				| path pin_delim index
				| path pin_delim name
				| index pin_delim INT
				| path pin_delim INT
				;
				
conn_attrs:
				| conn_attrs cap_load
				| conn_attrs drive_cell
				| conn_attrs slews;

drive_cell:
				DRIVER name;
				
cap_sec:
				|
				CAP cap_elems;
				
cap_elems:
				cap_elem
				| cap_elems cap_elem;

cap_elem:
				cap_id connection par_value
				| cap_id connection connection par_value;
				
cap_id:
				INT;

res_sec:
				|
				RES res_elems;
				
res_elems:
				res_elem
				| res_elems res_elem;

res_elem:
				res_id connection connection par_value;
				
res_id:
				INT;				

%%

namespace spef {
	int Spef::load(char * filename) {

		root_spef = this;

		// open a file handle to a particular file:
		FILE *myfile = fopen(filename, "r");
		// make sure it is valid:
		if (!myfile) {
			std::cout << "I can't open a.snazzle.file!" << std::endl;
			return -1;
		}
		// set flex to read from it instead of defaulting to STDIN:
		yyin = myfile;
		
		// parse through the input until there is no more:
		do {
			yyparse();
		} while (!feof(yyin));
		
		std::cout << "spef    " << root_spef->get_header("spef") << std::endl;
		std::cout << "design  " << root_spef->get_header("design") << std::endl;
		std::cout << "program " << root_spef->get_header("program") << std::endl;
		std::cout << "version " << root_spef->get_header("version") << std::endl;
		std::cout << "flow    " << root_spef->get_header("flow") << std::endl;
		std::cout << "2       " << root_spef->get_name_map("*2")  << std::endl;
		std::cout << "5       " << root_spef->get_name_map("*5")  << std::endl;
		std::cout << "13      " << root_spef->get_name_map("*13") << std::endl;
		
				
		printf("Finished\n");
	}
}

int main(int argc, char** argv) {

	// open a file handle to a particular file:
	FILE *myfile = fopen(argv[1], "r");
	// make sure it is valid:
	if (!myfile) {
		std::cout << "I can't open a.snazzle.file!" << std::endl;
		return -1;
	}
	// set flex to read from it instead of defaulting to STDIN:
	yyin = myfile;
	
	// parse through the input until there is no more:
	do {
		yyparse();
	} while (!feof(yyin));
	
	std::cout << "spef    " << root_spef->get_header("spef") << std::endl;
	std::cout << "design  " << root_spef->get_header("design") << std::endl;
	std::cout << "program " << root_spef->get_header("program") << std::endl;
	std::cout << "version " << root_spef->get_header("version") << std::endl;
	std::cout << "flow    " << root_spef->get_header("flow") << std::endl;
	std::cout << "2       " << root_spef->get_name_map("*2")  << std::endl;
	std::cout << "5       " << root_spef->get_name_map("*5")  << std::endl;
	std::cout << "13      " << root_spef->get_name_map("*13") << std::endl;
	
			
	printf("Finished\n");
}

void yyerror(const char* errstr) {
	printf("YUCK Error: %i, %s, %s\n", line_number, yylval, errstr);
}
